runnのカスタムランナーでAWS CLI の高レベルS3コマンド動かしてみた
小ネタ:runnのカスタムランナーでFizzBuzzやってみた | DevelopersIO
でまずは試しに使ってみましたが、runnの作者からは
正直に答えます。第一声が「おかしいやろ」でした。
— k1LoW (@k1LoW) March 1, 2024
と、ありがたいお言葉をいただいてしまったので、もうちょっと実用的なことも出しておかないなーと思い、やってみました。
何をやるか?
我々のプロダクトでは、API実行した結果、S3にファイルを配置するような処理があります。
こういったシナリオをテストしようと思うと、「今の」runnには専用のランナーがありません。なので、S3をアレコレするカスタムランナーを作ってみようと思います。
方針
カスタムランナーでは既存のランナーを組み合わた処理しか行えないため、目的を達成するにはどうするかを考えたところ、今回はシンプルにAWS CLIを使ったS3への高レベルコマンドをラップする形で作ってみようと思います。
AWS CLI で高レベル (S3) コマンドを使用する - AWS Command Line Interface
できたもの
結果できたカスタムランナーの定義がこちらです。
desc: AWS CLI S3 High-level command runner if: included vars: profile: '{{ parent.params.profile }}' command: '{{ keys(parent.nodes)[0] }}' paths: '{{ parent.nodes[keys(parent.nodes)[0]].paths }}' options: '{{ parent.nodes[keys(parent.nodes)[0]].options }}' steps: - exec: command: "aws --profile {{ vars.profile }} s3 {{ vars.command }} {{ vars.paths }} {{ vars.options }}" bind: exit_code: current.exit_code stdout: current.stdout stderr: current.stderr
このカスタムランナーを使用する側のシナリオはこちらです。
desc: S3アクセスのカスタムランナーを作ってみる runners: s3: path: s3-runner.yaml params: profile: myprofile steps: listObjects: desc: "特定バケットのオブジェクトリストを取得する" s3: ls: paths: s3://runn-s3-runner-sample options: '--recursive' test: | current.exit_code == 0 dump: current.stdout testExistentObject: desc: "特定オブジェクトの存在を確認する" s3: ls: paths: s3://runn-s3-runner-sample/some-object.txt test: | current.exit_code == 0 testNonExistentObject: desc: "特定オブジェクトが存在しないことを確認する" s3: ls: paths: s3://runn-s3-runner-sample/nonexistent-object.txt test: | current.exit_code != 0 GetObject: desc: "S3のオブジェクトを取得する" s3: cp: paths: s3://runn-s3-runner-sample/some-object.txt /tmp/some-object.txt test: | current.exit_code == 0 CatObject: desc: "取得したオブジェクトの内容を確認する" exec: command: 'cat /tmp/some-object.txt' test: current.stdout == 'foo'
実行した結果がこちらです。外部コマンドを使うexec
ランナーを使うため、--scopes runn:exec
オプションを付けてrunn
を実行します。
$ runn run --scopes run:exec custom-s3-runnder.yaml --verbose === S3アクセスのカスタムランナーを作ってみる (custom-s3-runnder.yaml) --- (0) ... ok 2024-03-04 09:27:20 0 any-object.txt 2024-03-03 16:32:18 0 some-object.txt === AWS CLI S3 runner (s3-runner.yaml) --- (0) ... ok --- (0) ... ok === AWS CLI S3 runner (s3-runner.yaml) --- (0) ... ok --- (0) ... ok === AWS CLI S3 runner (s3-runner.yaml) --- (0) ... ok --- (0) ... ok === AWS CLI S3 runner (s3-runner.yaml) --- (0) ... ok --- 取得したオブジェクトの内容を確認する (CatObject) ... fail Failure/Error: test failed on "S3アクセスのカスタムランナーを作ってみる".steps.CatObject "取得したオブジェクトの内容を確認する": condition is not true Condition: current.stdout == 'foo' │ ├── current.stdout => "" └── "foo" => "foo" Failure step (custom-s3-runnder.yaml): 38 CatObject: 39 desc: "取得したオブジェクトの内容を確認する" 40 exec: 41 command: 'cat /tmp/some-object.txt' 42 test: 43 current.stdout == 'foo' 1 scenario, 0 skipped, 1 failure
S3のオブジェクトの存在確認および、取得して内容を確認することができています。
最後のステップでは、あえて中身が空のファイルを使って、テストでエラーになるようにしています。
解説
今回のカスタムランナーのポイントは以下のとおりです。
- カスタムランナーの定義時と実行時に指定する内容を分別する
profile
: 複数回実行しても同じものを使う事が多いため、カスタムランナーの定義で指定させるcommand
、paths
、options
: 実行時に毎回変わるため、実行時変数で指定させる
- カスタムランナー定義、実行時に指定した値は、定義側で取得方法を使い分ける
- カスタムランナー定義時に
params
で指定した値{{ parent.params.<key> }}
で参照できる
- カスタムランナー実行側にてmapで指定した値
keys(parent.nodes)
でキーリストが取得できるparent.nodes.<key>
でmapの値を取り出せるparent.nodes[<key>]
でも可能
- これらを組み合わせることで柔軟な表現が可能になる
- カスタムランナー定義時に
- 実行結果を使用側で参照できるようbindしておく
exit_code
、stdout
、stderr
といったexec
コマンドの結果を、カスタムランナー使用側で参照するには、bind
で束縛する必要があります
まとめ
ある程度実用的なカスタムランナーも、比較的簡単に定義して使えることがわかりました。
ただ、定義するカスタムランナーに「直感的で使いやすいAPI」をつけるのは、設計者の腕の見せ所だなとも感じました。今回の例だと、あくまでAWS CLIのS3高レベルコマンドをラップしただけではありますが、カスタムランナーに与える値を、mapのキー、値どちらで渡したほうがより自然か?みたいな考え方は必要でした。
また、「これはカスタムランナーとして定義すべきものなのか?」も同時に考慮が必要だとも感じました。「DSLを作れる」というのは強い力ですが、ともすれば独りよがりなものにもなってしまいがちなものです。今後活用するにしても、使用箇所を見極めつつ、使ってみてはやっぱりやめたほうがいいとか、そういう試行錯誤していこうと思います。